<?php

namespace App\Repositories;


use App\Models\ConfigModel;
use App\Models\OperateModel;
use App\Models\StrategyModel;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;


class StatisticsRepository extends BaseRepository
{
    private $statisticsDuration = [
        'day' => 14,
        'week' => 8,
        'month' => 12
    ];

    private $colors = [
        'up' => [
            '#ce181e', '#e6353a', '#f9601f', '#fc8d03', '#fc8d03', '#fc8d03', '#fc8d03', '#fc8d03', '#fc8d03'
        ],
        'down' => [
            '#1a7023', '#399244', '#48a754', '#4cc95a', '#4cc95a', '#4cc95a', '#4cc95a', '#4cc95a', '#4cc95a'
        ]
    ];

    /**
     * @return array
     */
    public function getTopStatistics ()
    {
        $user = request()->user();
        $operateRepo = new OperateRepository();
        $montyLogReposition = new MoneyLogRepository();
        $initialMoney = ConfigModel::where('keyword', 'initial_money')
            ->where('admin_id', $user['id'])
            ->value('value');
        $enterMarketTime = ConfigModel::where('keyword', 'enter_market_time')
            ->where('admin_id', $user['id'])
            ->value('value');
        $currentMonthFirstDay = new \DateTime('first day of this month');
        $zeroTime = $currentMonthFirstDay->setTime(0, 0, 0)->format('Y-m-d H:i:s');
        $totalEarningMoney = OperateModel::where('admin_id', $user['id'])
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->sum('actual_earnings');
        $dealCount = $operateRepo->getDealCount();
        $monthDealCount = OperateModel::where('admin_id', $user['id'])
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->where('end_at', '>', $zeroTime)
            ->where('end_at', '<=', date('Y-m-d H:i:s'))
            ->count();
        $monthEarningMoney = OperateModel::where('admin_id', $user['id'])
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->where('end_at', '>', $zeroTime)
            ->where('end_at', '<=', date('Y-m-d H:i:s'))
            ->sum('actual_earnings');
        $successCount = $operateRepo->getSuccessCount();
        $failCount = $operateRepo->getFailCount();
        $serviceCharge = OperateModel::where('admin_id', $user['id'])
            ->where('is_finished', OperateModel::IS_FINISHED)
            ->sum('service_charge');
        if ($successCount + $failCount > 0) {
            $successRate = $successCount / ($successCount + $failCount);
        } else {
            $successRate = 0;
        }
        if (empty($initialMoney)) {
            $monthRate = 0;
            $rate = 0;
            $initialMoney = 0;
        } else {
            $month = date('m');
            $monthRate = $operateRepo->getRateByMonth($month);
            $rate = $operateRepo->getAllRate();
        }

        return [
            'initialMoney' => (float)$initialMoney,
            'enterMarketTime' => $enterMarketTime,
            'formatInitialMoney' => number_format($initialMoney, 2),
            'monthEarningMoney' => $monthEarningMoney / 100,
            'monthRate' => $monthRate / 100,
            'monthDealCount' => $monthDealCount,
            'balance' => $montyLogReposition->getBalance() / 100,
            'totalEarningMoney' => $totalEarningMoney / 100,
            'successRate' => number_format($successRate, 4) * 100,
            'serviceCharge' => $serviceCharge / 100,
            'rate' => $rate / 100,
            'dealCount' => $dealCount
        ];
    }

    /**
     * @param int $page
     * @return array
     */
    public function getMonthRate ($page = 1)
    {
        $user = request()->user();
//        $configRepo = new ConfigRepository();
//        $initialMoney = $configRepo->getConfigByKeyword('initial_money');
        $paginate = OperateModel::where('admin_id', $user['id'])
            ->whereNotNull('end_at')
            ->select(DB::raw('YEAR(end_at) as year, MONTH(end_at) as month, COUNT(*) as count, SUM(actual_earnings) as amount, SUM(total_rate) as rate'))
            ->groupBy('year', 'month')
            ->orderBy('end_at', 'desc')
            ->paginate(10, '*');
        $datas = $paginate->items();
        foreach ($datas as &$data) {
            $data['amount'] = round($data['amount'] / 100, 2);
            $data['rate'] = round($data['rate'] / 100, 2);
        }
        return [
            'list' => $datas,
            'total' => $paginate->total()
        ];
    }


    /**
     * @param $type
     * @return array
     */
    private function _getDbSelectOptions ($type)
    {
        if ($type == 'day') {
            $dbSelect = [DB::raw('YEAR(end_at) as year, WEEK(end_at) as week, DATE(end_at) as end_date, SUM(actual_earnings) as amount, SUM(real_rate) as rate')];
            $groupBy = ['end_date'];
            $limit = 14;
        } else if ($type == 'week') {
            $dbSelect = [DB::raw('YEAR(end_at) as year, WEEK(end_at) as week, SUM(actual_earnings) as amount, SUM(real_rate) as rate')];
            $groupBy = ['year', 'week'];
            $limit = 8;
        } else if ($type == 'month') {
            $dbSelect = [DB::raw('YEAR(end_at) as year, MONTH(end_at) as month, SUM(actual_earnings) as amount, SUM(real_rate) as rate')];
            $groupBy = ['year', 'month'];
            $limit = 12;
        }
        return [
            'dbSelect' => $dbSelect,
            'groupBy' => $groupBy,
            'limit' => $limit,
        ];
    }

    /**
     * @param $type
     * @return array
     */
    public function getChartStatistics ($type, $chartType)
    {
        $user = request()->user();
        $dbSelectOptions = $this->_getDbSelectOptions($type);
        $datas = OperateModel::where('admin_id', $user['id'])
            ->whereNotNull('end_at')
            ->select(...$dbSelectOptions['dbSelect'])
            ->groupBy(...$dbSelectOptions['groupBy'])
            ->orderBy('end_at', 'desc')
            ->limit($dbSelectOptions['limit'])
            ->get();
        $chartData = [];
        foreach ($datas->reverse() as $data) {
            $date = $this->_getDate($type, $data);
            $chartData[] = [
                'x' => $date,
                'y' => $data[$chartType] / 100
            ];
        }
        $duration = $this->statisticsDuration[$type];
        $chartData = $this->_formateChartData($type, $chartData, $duration);
        return $chartData;
    }

    /**
     * @param $type
     * @param $chartDatas
     * @return array
     */
    private function _formateChartData ($type, $chartDatas, $number)
    {
        $data = [];
        $dates = [];
        foreach ($chartDatas as $chartData) {
            $dates[$chartData['x']] = $chartData['y'];
        }
        $minNum = -1;
        $dateFormat = 'Y-m-d';
        if ($type == 'month') {
//            $minNum = -2;
            $dateFormat = 'Y-n';
        }
        $date = Carbon::now();
        // 设置日期为本周的星期一
        $monday = $date->startOfWeek(Carbon::MONDAY)->toDateString();
        for ($i = $number; $i > $minNum; $i--) {
            if($type == 'week') {
                $previousDate = date("Y-m-d", strtotime("$monday -$i week"));
            } else if ($type == 'month') {
                $d = Carbon::create(date('Y'), date('m'), date('d'));
                $d->subMonths($i);
                // 再减去1天（因为2024年7月31日，会计算错误....）
                $d->subDay(1);
                $previousDate = $d->format('Y-n');
            } else {
                $previousDate = date($dateFormat, strtotime(-$i . ' ' . $type));
            }
            if (empty($dates[$previousDate])) {
                $money = 0;
            } else {
                $money = $dates[$previousDate];
            }
            $data[] = [
                'x' => $previousDate,
                'y' => $money
            ];
        }
        return $data;
    }

    /**
     * @param $type
     * @param $data
     * @return string
     */
    private function _getDate ($type, $data)
    {
        $date = '';
        if ($type == 'day') {
            $date = $data['end_date'];
        } else if ($type == 'week') {
            $date = $this->_getDateFormYearWeek($data['year'], $data['week']);
        } else if ($type == 'month') {
            $date = $data['year'] . '-' . $data['month'];
        }
        return $date;
    }

    /**
     * @param $year
     * @param $week
     * @return string
     */
    private function _getDateFormYearWeek ($year, $week)
    {
        $firstDayOfWeek = date("Y-m-d", strtotime($year . "W" . $week + 1));
        $firstDayOfWeek = date("Y-m-d", strtotime($firstDayOfWeek . " -" . (date('N', strtotime($firstDayOfWeek)) - 1) . " days"));
//        $firstDayOfWeek .= "\n\r" . $week . "周";
        return $firstDayOfWeek;
    }

    /**
     * @return array|array[]
     */
    public function getRatePercent ()
    {
        $user = request()->user();
        $strategories = StrategyModel::pluck('name', 'id');
        $operateDatas = OperateModel::where('admin_id', $user['id'])
            ->select(DB::raw('strategy_id, ROUND(SUM(actual_earnings) / 100, 2) as earnAmount, ROUND(SUM(real_rate) / 100, 2) as rateAmount'))
            ->groupBy('strategy_id')
            ->get();
        $data = $this->_setValue($strategories, $operateDatas);
//        $data['amount'][0]['value'] = -129;
        $this->_setColor($data, 'amount');
        $this->_setColor($data, 'rate');
        return $data;
    }

    /**
     * @param $strategories
     * @param $operateDatas
     * @return array|array[]
     */
    private function _setValue ($strategories, $operateDatas)
    {
        $data = [
            'amount' => [],
            'rate' => []
        ];
        foreach ($strategories as $strategyId => $strategoryName) {
            foreach ($operateDatas as $operateData) {
                if ($operateData['strategy_id'] == $strategyId) {
                    $data['amount'][$strategyId] = [
                        'name' => $strategoryName,
                        'value' => $operateData['earnAmount'] ?? 0
                    ];

                    $data['rate'][$strategyId] = [
                        'name' => $strategoryName,
                        'value' => $operateData['rateAmount'] ?? 0
                    ];
                } else if (empty($data['amount'][$strategyId])) {
                    $data['amount'][$strategyId] = [
                        'name' => $strategoryName,
                        'value' => 0
                    ];

                    $data['rate'][$strategyId] = [
                        'name' => $strategoryName,
                        'value' => 0
                    ];
                }
            }
        }
        return $data;
    }

    /**
     * @param $data
     * @param $type
     */
    private function _setColor (&$data, $type)
    {
        usort($data[$type], function ($a, $b) {
            return $b['value'] - $a['value'];
        });
        $upNumber = 0;
        $downNumber = 0;
        foreach ($data[$type] as $key => &$one) {
            if ($one['value'] >= 0) {
                if (!empty($this->colors['up'][$upNumber])) {
                    $color = $this->colors['up'][$upNumber];
                } else {
                    $color = end($this->colors['up']);
                }
                $one['color'] = $color;
                $upNumber++;
            } else {
                $one['value'] = abs($one['value']);
                if (!empty($this->colors['down'][$downNumber])) {
                    $color = $this->colors['down'][$downNumber];
                } else {
                    $color = end($this->colors['down']);
                }
                $one['color'] = $color;
                $downNumber++;
            }
        }
    }
}
